package com.ofek.eventos.net.ocsf.client;



import java.io.IOException;
import java.util.HashMap;

import com.ofek.eventos.net.Request;
import com.ofek.eventos.net.ServiceCallBack;
/**
 * 
 * Class for representing a client, opens a connection to server
 * all the controllers in the project use this class instance to connect to the server 
 * sends messages and receives messages from server
 * @author Ofek Ron
 */
public class Client extends AbstractClient {
	/**
	 * save the response listener who waits for an answer from the server
	 */
	private final HashMap<Long,ServiceCallBack> responseAwaiting = new HashMap<Long,ServiceCallBack>();
	private long idProvider;
	private final Object idLock = new Object();

	/**
	 * static instance for server connection
	/**
	 * initialize a new Client
	 * @param host  - the machine's host
	 * @param port  - port of the server
	 */
	public Client(final String host, final int port) {
		super(host, port);
	}


	@Override
	protected void handleMessageFromServer(final Object msg) {
		final InvocationResult inv = (InvocationResult)msg;
		final ServiceCallBack listener = responseAwaiting.get(inv.id);
		responseAwaiting.remove(inv.id);
		if (listener==null) {
			System.out.println("server sent mssg while nobody was waiting for it " + msg);
			return;
		}
		if (inv.isOk)
			listener.onResult(inv.result);
		else
			listener.onError(inv.result);
	}


	/**
	 * sends a message to the server
	 * @param listener - the listener that is waiting for an answer
	 * @param inv - the method to invoke with its params
	 */
	public void sendToServer(final ServiceCallBack listener,final Request req) {
		long myId;
		synchronized (idLock) {
			myId = idProvider++;
		}
		responseAwaiting.put(myId, listener);
		try {
			sendToServer(new Invocation(myId,req,true));
		} catch (final IOException e) {
			e.printStackTrace();
			listener.onError(e);
		}
	}
	/**
	 * send and forget
	 * @param listener - the listener that is waiting for an answer
	 */
	public void sendAndForget(final Request req) {
		try {
			long myId;
			synchronized (idLock) {
				myId = idProvider++;
			}
			sendToServer(new Invocation(myId,req,false));
		} catch (final IOException e) {
			e.printStackTrace();

		}
	}


	public Object sendSync(final Request request) {
		return new Syncer().send(request);



	}
	private class Syncer {
		private Object result;
		public Object send(final Request request) {
			long myId;
			synchronized (idLock) {
				myId = idProvider++;
			}
			final Invocation inv = new Invocation(myId,request,true);
			responseAwaiting.put(myId, new ServiceCallBack() {

				@Override
				public void onResult(final Object r) {
					synchronized (Syncer.this) {
						result = r;
						Syncer.this.notify();
					}
				}

				@Override
				public void onError(final Object error) {
					synchronized (Syncer.this) {
						result = error;
						Syncer.this.notify();
					}
				}
			});
			try {
				synchronized (this) {
					sendToServer(inv);
					wait(60000);
				}
			} catch (final Exception e) {
				e.printStackTrace();
			} finally {
				synchronized (this) {Syncer.this.notify();}
			}
			return result;
		}
	}
}
